const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class MyPromise {

  // 私有属性，防止外部修改该属性的值
  #PromiseState = PENDING;
  #PromiseResult = undefined;
  #onFulfilledCallbacks = []; // 保存成功时的回调函数
  #onRejectedCallbacks = []; // 保存失败时的回调函数
  #handlers = []; // 保存then方法返回的新的Promise对象的handlers
  constructor(executor) {
    try {
      // 实例化Promise时必须传入一个执行器函数，该函数接收两个参数 resolve和reject用来改变promise的状态
      executor(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      this.reject(error);
    }
  }

  resolve (result) {
    this.#changeState(FULFILLED, result);
  }

  reject (reason) {
    this.#changeState(REJECTED, reason);
  }

  // 链式调用的then方法
  // 该方法接收两个参数 onFulfilled 和 onRejected，它们都是可选的，分别表示当Promise状态变为fulfilled和rejected时的回调函数
  then (onFulfilled, onRejected) {
    // Promise 规范如果 onFulfilled 和 onRejected 不是函数，就忽略他们。所谓“忽略”并不是什么都不干，对于onFulfilled来说“忽略”就是将value原封不动的返回，对于onRejected来说就是返回reason，onRejected因为是错误分支，我们返回reason应该throw一个Error
    // onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    // onRejected = typeof onRejected === 'function' ? onRejected : reason => {
    //   throw reason;
    // };
    let promise2

    promise2 = new MyPromise((resolve, reject) => {
      // if (this.#PromiseState === FULFILLED) {
      //   //onFulfilled(this.#PromiseResult);
      //   this.#onFulfilledCallbacks.push(() => {
      //     onFulfilled(this.#PromiseResult);
      //   })
      // } else if (this.#PromiseState === REJECTED) {
      //   //onRejected(this.#PromiseResult);
      //   this.#onRejectedCallbacks.push(() => {
      //     onRejected(this.#PromiseResult);
      //   })
      // } else {
      //   this.#onFulfilledCallbacks.push(() => {
      //     onFulfilled(this.#PromiseResult);
      //   })
      //   this.#onRejectedCallbacks.push(() => {
      //     onRejected(this.#PromiseResult);
      //   })
      // }

      this.#handlers.push({ onFulfilled, onRejected, resolve, reject, promise2 })
      this.#run()
    })
    return promise2;
  }

  // 状态改变的方法
  #changeState (state, result) {
    if (this.#PromiseState !== PENDING) return;
    this.#PromiseState = state;
    this.#PromiseResult = result;
    this.#run();
  }

  #run () {
    //console.log('run', this.#PromiseState);
    if (this.#PromiseState === PENDING) return;
    //console.log(this.#handlers.length);
    while (this.#handlers.length) {
      const { onFulfilled, onRejected, resolve, reject, promise2 } = this.#handlers.shift();
      //console.log('run2', this.#PromiseState);
      if (this.#PromiseState === FULFILLED) {
        //console.log('onFulfilled');
        this.#runOne(onFulfilled, resolve, reject, promise2)
        // if (typeof onFulfilled === 'function') {
        //   try {
        //     const data = onFulfilled(this.#PromiseResult);
        //     resolve(data)
        //   } catch (err) {
        //     reject(err)
        //   }

        // } else {
        //   // then resolve实参不是函数
        //   //如果 onFulfilled 不是函数且 promise1 成功执行， promise2 必须成功执行并返回相同的值
        //   resolve(this.#PromiseResult);
        // }
      } else if (this.#PromiseState === REJECTED) {
        this.#runOne(onRejected, resolve, reject, promise2)
        // if (typeof onRejected === 'function') {
        //   onRejected(this.#PromiseResult);
        // } else {
        //   // then reject实参不是函数
        //   //如果 onRejected 不是函数且 promise1 拒绝执行， promise2 必须拒绝执行并返回相同的据因
        //   reject(this.#PromiseResult);
        // }
      }
    }
  }

  #runOne (callback, resolve, reject, promise2) {
    //console.log("----");
    this.#runMicroTask(() => {
      //console.log("micro task");
      if (typeof callback !== 'function') {
        const settled = this.#PromiseState === FULFILLED ? resolve : reject;
        settled(this.#PromiseResult);
        return;
      }

      try {
        const data = callback(this.#PromiseResult);
        //console.log(data);
        // if (this.#isPromiseLike(data)) {
        //   data.then(resolve, reject);
        // } else {
        //   resolve(data);
        // }
        this.#resolvePromise(promise2, data, resolve, reject);
      } catch (error) {
        reject(error)
      }
    })
  }
  #resolvePromise (promise2, x, resolve, reject) {
    console.log(promise2, x);
    if (x === promise2) {
      throw new TypeError('Chaining cycle detected for promise');
    }

    if (x instanceof MyPromise) { // 
      x.then(y => {
        this.#resolvePromise(promise2, y, resolve, reject);
      }, reject)
    } else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) { // thenable
      try {
        // 2.3.3.1 把 x.then 赋值给 then
        var then = x.then;
      } catch (e) {
        // 2.3.3.2 如果取 x.then 的值时抛出错误 e ，则以 e 为据因拒绝 promise
        return reject(e);
      }

      if (typeof then === 'function') {
        let called = false;
        try {
          then.call(x,
            y => {
              if (called) return;
              called = true;
              this.#resolvePromise(promise2, y, resolve, reject);
            },
            r => {
              if (called) return;
              called = true;
              reject(r);
            }
          )
        } catch (error) {
          if (called) return;
          called = true;
          reject(error);
        }
      } else {
        resolve(x);
      }



    } else {
      return resolve(x);
    }
  }

  #runMicroTask (task) {
    //queueMicrotask(task);
    if (typeof process !== 'undefined' && typeof process.nextTick === 'function') {
      // node环境
      //console.log("node环境");
      return process.nextTick(task);
    } else if (typeof MutationObserver === "function") {
      //console.log("MutationObserver");
      const ob = new MutationObserver(task);
      const textNode = document.createTextNode('1')
      ob.observe(textNode, {
        characterData: true
      })
      textNode.data = '2'
    } else {
      //console.log("settime");
      setTimeout(task, 0);
    }
  }

  #isPromiseLike (value) {
    return value && typeof value.then === 'function';
  }
}




MyPromise.deferred = function () {
  let result = {};
  result.promise = new MyPromise((resolve, reject) => {
    result.resolve = resolve;
    result.reject = reject;
  });
  return result;
}

module.exports = MyPromise;